package cn.demoncat.util.lang;

import cn.demoncat.util.exception.ReflexRuntimeException;
import cn.demoncat.util.lang.constant.StringConstant;
import org.apache.commons.lang3.ArrayUtils;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;

/**
 * 反射Class工具
 * 
 * @author 延晓磊
 *
 * @since 2019年11月25日
 */
public class ReflectClassUtil {

	// 基本字段类型
	private static final String BASE_TYPE_PATTERN = "(String|Long|Integer|Boolean|Double|Character|Byte|Short|Float|BigDecimal)";
	// 集合字段类型
	private static final String COLLECTION_TYPE_PATTERN = ".*(List|Set|Map)";
	// 代理 class 的名称
	private static final List<String> PROXY_CLASS_NAMES = Arrays.asList(
			"net.sf.cglib.proxy.Factory",
			"org.springframework.cglib.proxy.Factory",
			"javassist.util.proxy.ProxyObject",
			"org.apache.ibatis.javassist.util.proxy.ProxyObject");

	/**
	 * 新建对象
	 * 
	 * @param clazz 	类型
	 * @param params	构造参数
	 * @return 对象
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static <T> T instance(Class<T> clazz, Object... params) {
		// 无参构造
		if (ArrayUtils.isEmpty(params)) {
			// 创建对象
			try {
				return clazz.newInstance();
			} catch (InstantiationException | IllegalAccessException e) {
				// 失败
				throw new ReflexRuntimeException("反射创建对象失败：class = " + clazz.getName(), e);
			}
		}
		// 有参构造
		try {
			// 构建器（参数类型）
			Constructor<T> constructor = clazz.getDeclaredConstructor(getClasses(params));
			// 实例化
			return constructor.newInstance(params);
		} catch (Exception e) {
			// 失败
			throw new ReflexRuntimeException("反射创建对象失败：class = " + clazz.getName() + ", params = " + ArrayUtil.toString(params), e);
		}
	}

	/**
	 * 新建对象
	 *
	 * @param clazz 	类型
	 * @param fields	字段/值
	 * @return 对象
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static <T> T instance(Class<T> clazz, Map<String,Object> fields) {
		T instance = instance(clazz);
		ReflectFieldUtil.set(instance, fields);
		return instance;
	}

	/**
	 * 新建对象
	 *
	 * @param className
	 *            类型
	 * @return 对象
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	@SuppressWarnings("unchecked")
	public static <T> T instance(String className) {
		// 创建对象
		try {
			return (T) Class.forName(className).newInstance();
		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
			// 失败
			throw new ReflexRuntimeException("反射创建对象失败：className = " + className, e);
		}
	}

	/**
	 * 新建对象
	 *
	 * @param className	类型
	 * @param fields	字段/值
	 * @return 对象
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static <T> T instance(String className, Map<String,Object> fields) {
		T instance = instance(className);
		ReflectFieldUtil.set(instance, fields);
		return instance;
	}

	/**
	 * 获得对象数组的类数组
	 *
	 * @param objects 对象数组，null元素返回Object类型
	 *
	 * @return 类数组
	 */
	public static Class<?>[] getClasses(Object... objects) {
		Class<?>[] classes = new Class<?>[objects.length];
		Object obj;
		for (int i = 0; i < objects.length; i++) {
			obj = objects[i];
			if (null == obj) {
				classes[i] = Object.class;
			} else {
				classes[i] = obj.getClass();
			}
		}
		return classes;
	}

	/**
	 * 判断继承关系
	 * 
	 * @param superClass
	 *            父类
	 * @param childClass
	 *            子类
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月10日
	 */
	public static boolean isSupper(Class<?> superClass, Class<?> childClass) {
		return superClass.isAssignableFrom(childClass);
	}

	/**
	 * 获取直接继承的父类和接口
	 * 
	 * 1、只包含直接继承的父类（接口），不包含父类的父类（接口）
	 * 
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月1日
	 */
	public static Set<Class<?>> getSuppers(Class<?> clazz) {
		return ReflectionUtils.getSuperTypes(clazz);
	}

	/**
	 * 获取继承的父类和接口
	 * 
	 * 1、包含直接继承的父类（接口），包含父类的父类（接口）
	 * 
	 * @param clazz
	 * @param predicates
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月1日
	 */
	@SuppressWarnings("unchecked")
	public static Set<Class<?>> getSuppers(Class<?> clazz, Predicate<Class<?>> predicates) {
		return ReflectionUtils.getAllSuperTypes(clazz, predicates);
	}

	/**
	 * 获取packageName包下所有clazz的子类
	 * 
	 * @param packageName	包名前缀，空表示根路径
	 * @param clazz			父类（接口）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月1日
	 */
	public static <T>Set<Class<? extends T>> getSubs(String packageName, Class<T> clazz){
		 return new Reflections(StringUtil.trimToEmpty(packageName)).getSubTypesOf(clazz);
	}
	
	/**
	 * 获取packageName包下注解anno的类（直接注解）
	 * 
	 * @param packageName	包名前缀，空表示根路径
	 * @param anno			注解
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月1日
	 */
	public static Set<Class<?>> getAnnotateds(String packageName, Annotation anno){
		 return new Reflections(StringUtil.trimToEmpty(packageName)).getTypesAnnotatedWith(anno);
	}
	
	/**
	 * 获取packageName包下注解anno的类（包含父类注解）
	 * 
	 * @param packageName	包名前缀，空表示根路径
	 * @param anno			注解
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月1日
	 */
	public static Set<Class<?>> getAllAnnotations(String packageName, Annotation anno){
		 return new Reflections(StringUtil.trimToEmpty(packageName)).getTypesAnnotatedWith(anno,true);
	}
	
	/**
	 * 获取类的jar包位置
	 * 
	 * <pre>
	 * 场景：
	 * 程序编写正常，Jar包依赖正常，类及方法也有，但是运行时报错找不到方法
	 * 原因：
	 * 项目依赖的多个Jar中有相同的Class，但是版本不同，所以方法不同，并且是缺少方法的Jar包被优先加载
	 * 解决：
	 * 调用本方法查看调用class的JAR包 > 通过Maven管理来排除错误的依赖
	 * </pre>
	 * 
	 * @param clazz
	 * @return 依赖类的Jar包位置/当前项目类的ClassPath
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月8日
	 */
	public static String getClassJar(Class<?> clazz) {
		// 类信息.代码.位置
		URL url = clazz.getProtectionDomain().getCodeSource().getLocation();
		return url == null ? null : url.getPath();
	}

	/**
	 * 获取类的绝对路径
	 * 
	 * @param clazz
	 * @return ClassPath + 类的包的相对位置
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月8日
	 */
	public static String getClassPath(Class<?> clazz) {
		return getClassPath() + getClassRelativePath(clazz);
	}

	/**
	 * 获取类的相对路径
	 * 
	 * @param clazz
	 * @return 类的包的相对位置：获取包名并将.替换为/
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月8日
	 */
	public static String getClassRelativePath(Class<?> clazz) {
		return clazz.getPackage().getName().replace(StringConstant.POINT, StringConstant.SEPARATOR);
	}

	/**
	 * 获取当前的类加载器
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月8日
	 */
	public static ClassLoader getClassLoader() {
		// 当前线程.类加载器
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		if (classLoader == null) {
			// 当前类.类加载器
			classLoader = ReflectClassUtil.class.getClassLoader();
		}
		return classLoader;
	}

	/**
	 * 获取资源的绝对路径URL
	 *
	 * 注意：jar包内的文件，无法直接用File读取
	 *
	 * @param resource 	相对路径："" = classpath/，com/A.class = classpath/com包/目录下的A.class文件
	 * 
	 * @return 资源的绝对路径，资源不存在返回null：E:/webapps/java/demoncat-test/target/test-classes/test/cert/ 或 /webapps/tt-dz-pay-web/tt-dz-pay-web-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/test/cert/
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月8日
	 */
	public static String getResourceUrl(String resource) {
		// 类加载器.在target/classes/下搜索资源
		URL url = getClassLoader().getResource(StringUtil.toEmpty(resource));
		if (url == null) {
			// 未找到资源
			return null;
		}else {
			// Win路径前缀/处理
			String path = url.getPath();
			if (StringConstant.SEPARATOR.equals(StringUtil.get(path, 0)) && StringConstant.COLON.equals(StringUtil.get(path, 2))) {
				// "/E:/webapps/util/demoncat-util/demoncat-util-lang/target/classes/"
				path = path.substring(1);
			}
			return path;
		}
	}

	/**
	 * 获取ClassPath的绝对路径
	 *
	 * 注意：jar包内的文件，无法直接用File读取
	 *
	 * @return D:/Data/webapps/javas/demoncat-util/target/classes/ 或 /webapps/demoncat-util/demoncat-util-web-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月8日
	 */
	public static String getClassPath() {
		// Thread.currentThread().getContextClassLoader().getResource("").getPath()
		return getResourceUrl(null);
	}
	
	/**
	 * 获取类名驼峰
	 * 
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月31日
	 */
	public static String getHumpName(Class<?> clazz) {
		return StringUtil.firstLower(clazz.getSimpleName());
	}

	/**
	 * 判断类是否存在
	 *
	 * @param className	类全名
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年8月31日
	 */
	public static boolean isPresent(String className){
		try {
			Class.forName(className, false, getClassLoader());
		} catch (ClassNotFoundException e) {
			// 加载失败，类不存在
			return false;
		}
		return true;
	}

	/**
	 * 加载类
	 *
	 * @param className
	 * @return
	 */
	public static Class<?> loadClass(String className){
		try {
			return Class.forName(className, true, getClassLoader());
		} catch (ClassNotFoundException e) {
			throw new ReflexRuntimeException("反射获取Class失败：className = " + className, e);
		}
	}

	/**
	 * 判断类型是否为基本类型
	 *
	 * 校验：String|Long|Integer|Boolean|Double|Character|Byte|Short|Float|BigDecimal
	 *
	 * @param fieldType	字段类型
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static boolean isBase(String fieldType) {
		return fieldType.matches(BASE_TYPE_PATTERN);
	}

	/**
	 * 判断类型是否为集合类型
	 *
	 * 校验：*List|*Set|*Map
	 *
	 * @param fieldType	字段类型
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static boolean isCollection(String fieldType) {
		return fieldType.matches(COLLECTION_TYPE_PATTERN);
	}

	/**
	 * 判断类型是否为POJO类型
	 *
	 * 校验：对象和数组（非基本类型、集合类型）
	 *
	 * @param fieldType	字段类型
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static boolean isPojo(String fieldType) {
		return !(isCollection(fieldType) || isBase(fieldType));
	}

	/**
	 * 判断字段是否为基本类型
	 *
	 * 校验：String|Long|Integer|Boolean|Double|Character|Byte|Short|Float|BigDecimal
	 *
	 * @param field 字段
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static boolean isBase(Field field) {
		return isBase(field.getType().getSimpleName());
	}

	/**
	 * 判断字段是否为集合类型
	 *
	 * 校验：*List|*Set|*Map
	 *
	 * @param field 字段
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static boolean isCollection(Field field) {
		return isCollection(field.getType().getSimpleName());
	}

	/**
	 * 判断字段是否为POJO类型
	 *
	 * 校验：对象和数组（非基本类型、集合类型）
	 *
	 * @param field 字段
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static boolean isPojo(Field field) {
		return isPojo(field.getType().getSimpleName());
	}

	/**
	 * 判断是否为代理对象
	 *
	 * @param clazz
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年05月24日
	 */
	public static boolean isProxy(Class<?> clazz) {
		for (Class<?> cls : clazz.getInterfaces()) {
			if (PROXY_CLASS_NAMES.contains(cls.getName())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 获取集合属性的元素类型
	 *
	 * @param listField	集合字段
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static Class<?> getListFieldElementType(Field listField) {
		// 获取完整的字段类型(包含集合的泛型)
		String genericType = listField.getGenericType().toString();
		try {
			// 获取泛型的类型
			return Class.forName(genericType.substring(genericType.indexOf('<') + 1, genericType.length() - 1));
		} catch (Exception e) {
			throw new ReflexRuntimeException("获取集合类型失败：Field = " + listField, e);
		}
	}

	/**
	 * 获取原始class
	 *
	 * @param clazz
	 * @return 如果是代理的class，返回父 class，否则返回自身
	 *
	 * @author 延晓磊
	 * @since 2022年05月24日
	 */
	public static Class<?> getOriginClass(Class<?> clazz) {
		return isProxy(clazz) ? clazz.getSuperclass() : clazz;
	}

	/**
	 * 获取类的泛型
	 *
	 * @param clazz 目标类
	 * @param index 泛型索引
	 * @param <T>   泛型类型
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年05月24日
	 */
	@SuppressWarnings("unchecked")
	public static <T>Class<T> getGeneric(Class<?> clazz, int index){
		// 获取原始类
		clazz = getOriginClass(clazz);
		// 获取类的泛型
		Type generic = clazz.getGenericSuperclass();
		if (generic instanceof ParameterizedType) {
			return (Class<T>)(((ParameterizedType)generic).getActualTypeArguments()[index]);
		}
		return null;
	}

}
